/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "ObjectConfigManager.h"

using namespace std;
using namespace OBSPlugin;

namespace NUM {
    constexpr int I1 = 1;
    constexpr int I2 = 2;
    constexpr int I3 = 3;
    constexpr int I4 = 4;
    constexpr int I5 = 5;
    constexpr int I6 = 6;
    constexpr int I8 = 8;
    constexpr int I10 = 10;
    constexpr int I16 = 16;
    constexpr int I20 = 20;
    constexpr int I32 = 32;
    constexpr int I50 = 50;
    constexpr int I64 = 64;
    constexpr int I100 = 100;
    constexpr int I120 = 120;
    constexpr int I128 = 128;
    constexpr int I200 = 200;
    constexpr int I300 = 300;
    constexpr int I480 = 480;
    constexpr int I500 = 500;
    constexpr int I512 = 512;
    constexpr int I1000 = 1000;
    constexpr int I1024 = 1024;
    constexpr int I2000 = 2000;
    constexpr int I3000 = 3000;
    constexpr int I4000 = 4000;
    constexpr int I4800 = 4800;
    constexpr int I6000 = 6000;
    constexpr int I8000 = 8000;
    constexpr int I10000 = 10000;
    constexpr int I40000 = 40000;
    constexpr int I50000 = 50000;
    constexpr int I80000 = 80000;
    constexpr int I86400 = 86400;
    constexpr int I100000 = 100000;
    constexpr int I800000 = 800000;
    constexpr int I1000000 = 1000000;
    constexpr int I10000000 = 10000000;
    constexpr int I100000000 = 100000000;
    constexpr int MB = 1024 * 1024;
    constexpr int GB = 1024 * 1024 * 1024;

    constexpr int MP_TASK_FAILED_NO_REPORT = -15;
    constexpr int RET_BUSY = 101;
    const std::string OBS_CONFIG_FILE = "/opt/obs-conf/";
    constexpr auto DEFAULT_PREFIX_SPLITTER = "/";
}

struct TmpIntInfo {
    std::string key;
    int minValue;
    int maxValue;
    int defaultValue;
};

struct TmpStringInfo {
    std::string key;
    std::string defaultValue;
};

void ObjectConfigManager::InitBackupConfigurations()
{
    TmpIntInfo cfgTbl[] = {
        {"DME_OBS_SCAN_CTRL_MAX_ENTRIES_FULLBKUP", NUM::I1, NUM::I100000, NUM::I10000},
        {"DME_OBS_SCAN_CTRL_MAX_ENTRIES_INCRBKUP", NUM::I1, NUM::I100000, NUM::I1000},
        {"DME_OBS_SCAN_CTRL_MIN_ENTRIES_FULLBKUP", NUM::I1, NUM::I50000, NUM::I10000},
        {"DME_OBS_SCAN_CTRL_MIN_ENTRIES_INCRBKUP", NUM::I1, NUM::I50000, NUM::I1000},
        {"DME_OBS_SCAN_DEFAULT_META_FILE_SIZE", NUM::I1, NUM::GB, NUM::GB},
        {"DME_OBS_SCAN_LIBNFS_OPENDIR_REQ_CNT", NUM::I1, NUM::I100000, NUM::I4000},
        {"DME_OBS_SCAN_LIST_OBJECTS_NUM", NUM::I100, NUM::I1000, NUM::I1000},
        {"DME_OBS_SCAN_X6000_OPENDIR_REQ_CNT", NUM::I1, NUM::I10000, NUM::I500},
        {"DME_OBS_SCAN_X6000_MAX_WRITE_QUEUE_SIZE", NUM::I1, NUM::I10000, NUM::I1000},
        {"DME_OBS_SCAN_X6000_MAX_SCAN_QUEUE_SIZE", NUM::I1, NUM::I50000, NUM::I10000},
        {"DME_OBS_SCAN_X6000_MIN_SCAN_QUEUE_SIZE", NUM::I1, NUM::I40000, NUM::I8000},
        {"DME_OBS_SCAN_X6000_WRITE_QUEUE_SIZE", NUM::I1, NUM::I1000, NUM::I100},
        {"DME_OBS_SCAN_X6000_DIR_ENTRY_READ_COUNT", NUM::I1, NUM::I512, NUM::I512},
        {"DME_OBS_SCAN_X6000_DEFAULT_META_FILE_SIZE", NUM::I1, NUM::GB, NUM::GB},
        {"DME_OBS_SCAN_X8000_MAX_WRITE_QUEUE_SIZE", NUM::I1, NUM::I100000, NUM::I10000},
        {"DME_OBS_SCAN_X8000_MAX_SCAN_QUEUE_SIZE", NUM::I1, NUM::I1000000, NUM::I100000},
        {"DME_OBS_SCAN_X8000_MIN_SCAN_QUEUE_SIZE", NUM::I1, NUM::I800000, NUM::I80000},
        {"DME_OBS_SCAN_X8000_WRITE_QUEUE_SIZE", NUM::I1, NUM::I1000, NUM::I1000},
        {"DME_OBS_SCAN_X8000_DIR_ENTRY_READ_COUNT", NUM::I1, NUM::I1024, NUM::I1024},
        {"DME_OBS_SCAN_X8000_DEFAULT_META_FILE_SIZE", NUM::I1, NUM::GB, NUM::GB},
        {"DME_OBS_SCAN_X8000_PRODUCER_THREAD_COUNT", NUM::I1, NUM::I32, NUM::I32},
        {"DME_OBS_SCAN_X3000_WRITE_QUEUE_SIZE", NUM::I1, NUM::I1000, NUM::I100},
        {"DME_OBS_SCAN_X3000_DEFAULT_META_FILE_SIZE", NUM::I1, NUM::I1000 * NUM::MB, NUM::I100 * NUM::MB},
        {"DME_OBS_SCAN_X3000_PRODUCER_THREAD_COUNT", NUM::I1, NUM::I32, NUM::I4},
        {"DME_OBS_SCAN_CTRL_FILE_TIME_SEC", NUM::I1, NUM::I120, NUM::I5},
        {"DME_OBS_SCAN_CTRL_FILE_SIZE", NUM::I1000000, NUM::I100000000, NUM::I10000000},
        {"DME_OBS_BACKUP_NAS_SERVER_CHECK_MAX_ERR_COUNT", NUM::I10, NUM::I10000, NUM::I100},
        {"DME_OBS_BACKUP_MAX_ALI_QPS_RW", NUM::I100, NUM::I10000, NUM::I2000},
        {"DME_OBS_BACKUP_MAX_OBS_QPS_GET", NUM::I100, NUM::I10000, NUM::I10000},
        {"DME_OBS_BACKUP_MAX_OBS_QPS_PUT", NUM::I100, NUM::I10000, NUM::I6000},
        {"DME_OBS_BACKUP_MAX_TOTAL_BLOCK_BUFFER_CNT", NUM::I100, NUM::I10000, NUM::I500},
        {"DME_OBS_BACKUP_MAX_TOTAL_BLOCK_BUFFER_SIZE", NUM::I10 * NUM::MB, NUM::I1024 * NUM::MB, NUM::I50 * NUM::MB},
        {"DME_OBS_BACKUP_BLOCK_SIZE", NUM::I1 * NUM::MB, NUM::I8 * NUM::MB, NUM::I5 * NUM::MB},
        {"DME_OBS_BACKUP_SERVER_MAX_MEMORY_SIZE", NUM::I10 * NUM::MB, NUM::I1024 * NUM::MB, NUM::I50 * NUM::MB},
        {"DME_OBS_BACKUP_SERVER_THREAD_NUM", NUM::I2, NUM::I64, NUM::I32},
        {"DME_OBS_BACKUP_META", 0, NUM::I1, NUM::I1},
        {"BACKUP_STUCK_TIME", NUM::I120, NUM::I86400, NUM::I3000},
        {"DME_OBS_AGGR_THREAD_POOL_CNT", NUM::I1, NUM::I128, NUM::I8},
        {"DME_OBS_PREFIX_SPLIT_DEPTH", NUM::I1, NUM::I16, NUM::I3},
        {"DME_OBS_BACKUP_BLOCK_NUM", NUM::I1, NUM::I10000, NUM::I10000},
    };

    for (size_t i = 0; i < (sizeof(cfgTbl) / sizeof(cfgTbl[0])); i++) {
        m_tableIntKeyInfo[cfgTbl[i].key] = {cfgTbl[i].minValue, cfgTbl[i].maxValue, cfgTbl[i].defaultValue};
    }
}

void ObjectConfigManager::InitBackupConfigurations2()
{
    TmpIntInfo cfgTbl[] = {
        {"DME_OBS_COMPRESS_META_DATA", 0, 1, 0},
        {"DME_OBS_ENCODING_FLAG", 0, 1, 0},
        {"DME_OBS_SCAN_PARALLEL_SWITCH", 0, 1, 1},
    };

    for (size_t i = 0; i < (sizeof(cfgTbl) / sizeof(cfgTbl[0])); i++) {
        m_tableIntKeyInfo[cfgTbl[i].key] = {cfgTbl[i].minValue, cfgTbl[i].maxValue, cfgTbl[i].defaultValue};
    }
}

void ObjectConfigManager::InitStringBackupConfigurations()
{
    TmpStringInfo cfgTbl[] = {
        ("DME_OBS_SCAN_CTRL_MAX_DATASIZE", "10737418240"),
        ("DME_OBS_SCAN_CTRL_MIN_DATASIZE", "5368709120"),
        ("DME_OBS_PREFIX_SPLITTER", NUM::DEFAULT_PREFIX_SPLITTER),
        ("OBS_SQLITE_DIR", "/tmp"),
        ("DME_OBS_ENCODING_CFG", ""),
        ("DME_OBS_BACKUP_EXCLUSE_META", "Content-Length,Content-Range,ETag,crc64ecma"),
    };

    for (size_t i = 0; i < (sizeof(cfgTbl) / sizeof(cfgTbl[0])); i++) {
        m_tableStringKeyInfo[cfgTbl[i].key] = cfgTbl[i].defaultValue;
    }
}

std::string ObjectConfigManager::GetInnerConfigStr(const std::string& key)
{
    if (m_tableStringKeyInfo.empty()) {
        InitStringBackupConfigurations();
    }
    std::string result = GetConfigStrFromFile(key);
    if (result.empty()) {
        return m_tableStringKeyInfo[key];
    }
    return result;
}

int ObjectConfigManager::GetInnerConfigInt(const std::string& key)
{
    if (m_tableIntKeyInfo.empty()) {
        InitBackupConfigurations();
        InitBackupConfigurations2();
    }
    std::string result = GetConfigStrFromFile(key);
    if (result.empty()) {
        return m_tableIntKeyInfo[key].defaultValue;
    }
    int value;
    try {
        value = std::stoi(result);
    } catch (const std::invalid_argument& ia) {
        ERRLOG("Invalid argument: %s", ia.what());
        return m_tableIntKeyInfo[key].defaultValue;
    } catch (const std::out_of_range& oor) {
        ERRLOG("Out of range: %s", oor.what());
        return m_tableIntKeyInfo[key].defaultValue;
    }

    if (value > m_tableIntKeyInfo[key].maxValue || value < m_tableIntKeyInfo[key].minValue) {
        value = m_tableIntKeyInfo[key].defaultValue;
    }
    return value;
}

std::string ObjectConfigManager::GetConfigStrFromFile(const std::string& fileName)
{
    try {
        std::string filePath = NUM::OBS_CONFIG_FILE + fileName;
        std::ifstream file(filePath);
        if (!file.is_open()) {
            throw std::runtime_error("can't open file: " + filePath);
        }
        std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        return content;
    } catch (std::exception& e) {
        ERRLOG("read file failed: %s", e.what());
        return "";
    }
}
